import java.sql.Date;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;

/**
 * La classe gestisce tutte le chiamate al web service relative alla risorsa dei
 * film. L’accesso alla risorsa è ottenuto tramite il path relativo /movies.
 * 
 */
@Path("/movies")
public class MoviesResource {

	/**
	 * Ottiene la lista di tutti film contenuti nel database o di un
	 * sottoinsieme di esso, permettendo un filtraggio per categoria, periodo di
	 * uscita (compreso tra due date), per titolo, attore e regista. È possibile
	 * richiedere un numero limitato di risultati a partire da un punto
	 * qualunque della lista totale seguendo la metafora della suddivisione in
	 * “pagine” del risultato della ricerca. Tutti i parametri sono opzionali.
	 * 
	 * @param movieTypes
	 *            elenco dei generi cinematografici desiderati
	 * @param startDate
	 *            data iniziale per la ricerca filtrata
	 * @param endDate
	 *            data finale per la ricerca filtrata
	 * @param index
	 *            indice del primo elemento da ricevere rispetto all'elenco
	 *            complessivo del risultato
	 * @param length
	 *            numero di elementi da ricevere a partire da quello in
	 *            posizione startIndex rispetto all'elenco complessivo del
	 *            risultato
	 * @param title
	 *            titolo del film
	 * @param director
	 *            nome del regista
	 * @param actor
	 *            nome di un attore presente nel film
	 * @return elenco dei film ottenuti dopo il filtraggio desiderato
	 */
	@GET
	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	public Response getMovies(
			@QueryParam(value = "movieType") final List<String> movieTypes,
			@DefaultValue("") @QueryParam("startDate") String startDate,
			@DefaultValue("") @QueryParam("endDate") String endDate,
			@DefaultValue("0") @QueryParam("startIndex") String index,
			@DefaultValue("0") @QueryParam("length") String length,
			@DefaultValue("") @QueryParam("title") String title,
			@DefaultValue("") @QueryParam("director") String director,
			@DefaultValue("") @QueryParam("actor") String actor) {

		StatusCode code;
		code = StatusCode.OK;

		ResponseBuilder responseBuilder;

		// controllo la validità sintattica dei parametri
		code = parametersChecking(endDate, startDate, index, length);
		MySQLConnection connection = null;
		responseBuilder = Response.status(code.getValue());
		// Se i parametri sono sintatticamente corretti
		if (code.getValue() < 300) {
			// query al database con aggiornamento di statusCode

			int lengthInt = Integer.parseInt(length);
			MovieFilter filter = createFilter(movieTypes, startDate, endDate,
					index, length, title, director, actor);
			try {
				connection = new MySQLConnection();
				List<Movie> movies = QueryMaker.getMovies(connection, filter);
				if (movies.size() == 0) {
					filter = createFilter(movieTypes, startDate, endDate, "0",
							"0", title, director, actor);
					int count = QueryMaker.getMoviesCount(connection, filter);

					// la query ha fornito lista vuota perchè non c'erano film
					// corrispondenti al filtro
					if (count == 0)
						code = StatusCode.OK_END_OF_MOVIES;

					// la query ha fornito la lista vuota perchè sono andato
					// oltre i limiti
					else
						code = StatusCode.INDEX_OUT_OF_BOUNDS;
				}
				// se la dimensione di movies.size è minore di length e length è
				// un valore significativo
				else if (movies.size() < lengthInt && lengthInt > 0)
					code = StatusCode.OK_END_OF_MOVIES;

				responseBuilder.entity(new GenericEntity<List<Movie>>(movies) {
				});

				responseBuilder.status(code.getValue());

			} catch (SQLException e) {
				e.printStackTrace();
				responseBuilder = Response.serverError();
			} catch (NumberFormatException ex) {
				code = StatusCode.PARAMETERS_ERROR;
				responseBuilder.status(code.getValue());
			}
		}

		if (connection != null)
			connection.closeConnection();
		return responseBuilder.build();
	}

	/**
	 * Crea l'oggetto utilizzato per rappresentare i parametri del filtro per la
	 * ricerca nel database di film
	 * 
	 * @param movieTypes
	 *            elenco dei generi cinematografici desiderati
	 * @param startDate
	 *            data iniziale per la ricerca filtrata
	 * @param endDate
	 *            data finale per la ricerca filtrata
	 * @param index
	 *            indice del primo elemento da ricevere rispetto all'elenco
	 *            complessivo del risultato
	 * @param length
	 *            numero di elementi da ricevere a partire da quello in
	 *            posizione startIndex rispetto all'elenco complessivo del
	 *            risultato
	 * @param title
	 *            titolo del film
	 * @param director
	 *            nome del regista
	 * @param actor
	 *            nome di un attore presente nel film
	 * @return elenco dei film ottenuti dopo il filtraggio desideratotor
	 * @param actor
	 * @return oggetto rappresentativo del filtro sui film
	 */
	private MovieFilter createFilter(List<String> movieTypes, String startDate,
			String endDate, String index, String length, String title,
			String director, String actor) {

		MovieFilter filter = new MovieFilter();

		filter.setStartIndex(Integer.parseInt(index));
		filter.setLength(Integer.parseInt(length));

		Date start = stringToDate(startDate, "dd/MM/yyyy");
		Date end = stringToDate(endDate, "dd/MM/yyyy");
		filter.setEndDate(end);
		filter.setStartDate(start);

		List<MovieType> types = new ArrayList<MovieType>();
		for (String s : movieTypes) {
			types.add(new MovieType(s));
		}
		filter.setTypes(types);

		// creo la lista di attori solo se l'attore inviato non � la stringa
		// vuota
		if (actor.length() > 0) {
			List<String> actors = new ArrayList<String>();
			actors.add(actor);
			filter.setActors(actors);
		}

		filter.setTitle(title);

		filter.setDirector(director);

		return filter;
	}

	/*
	 * Restituisce la data in un oggetto java.sql.Date o null se la data non �
	 * convertibile
	 */
	private Date stringToDate(String dateString, String format) {
		Date date = null;
		if (!dateString.equals("")) {
			try {
				java.util.Date utilDate = new SimpleDateFormat(format)
						.parse(dateString);
				date = new Date(utilDate.getTime());
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return date;
	}

	/**
	 * Verifica la validità dei parametri inseriti
	 * 
	 * @param endDate
	 *            data finale per la ricerca filtrata
	 * @param startDate
	 *            data iniziale per la ricerca filtrata
	 * @param index
	 *            indice del primo elemento da ricevere rispetto all'elenco
	 *            complessivo del risultato
	 * @param length
	 *            numero di elementi da ricevere a partire da quello in
	 *            posizione startIndex rispetto all'elenco complessivo del
	 *            risultato
	 * @return un codice rappresentativo della correttezza o meno dei parametri.
	 */
	private StatusCode parametersChecking(String endDate, String startDate,
			String index, String length) {
		StatusCode code = StatusCode.OK;
		java.util.Date start = null, end = null;
		try {
			if (!startDate.equals("")) {
				start = new SimpleDateFormat("dd/MM/yyyy").parse(startDate);
			}
			if (!endDate.equals("")) {
				end = new SimpleDateFormat("dd/MM/yyyy").parse(endDate);
			}
			if (!startDate.equals("") && !endDate.equals("")) {
				if (start.after(end))
					throw new Exception();
			}

			int indexInt = Integer.parseInt(index);
			int lengthInt = Integer.parseInt(length);

			if (indexInt < 0 || lengthInt < 0)
				throw new Exception();
		} catch (Exception e) {
			code = StatusCode.PARAMETERS_ERROR;
		}
		return code;
	}

	/**
	 * Ottiene la lista di tutti i generi cinematografici presenti nel database.
	 * 
	 * @return lista dei generi cinematografici
	 */
	@Path("/genres")
	@GET
	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	public Response getGenres() {
		MySQLConnection connection = null;

		StatusCode code = StatusCode.OK;
		ResponseBuilder responseBuilder = Response.status(code.getValue());
		try {
			connection = new MySQLConnection();
			List<MovieType> genres = QueryMaker.getGenres(connection);
			responseBuilder.entity(new GenericEntity<List<MovieType>>(genres) {
			});
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			responseBuilder = Response.serverError();
		}

		if (connection != null)
			connection.closeConnection();
		return responseBuilder.build();
	}

	/**
	 * Ottiene informazioni dettagliate, comprensive di URL per la
	 * visualizzazione di thumbnail, trailer e playbills, di un film di cui
	 * viene specificato l’id.
	 * 
	 * @param movieID
	 *            identificativo del film di cui si desiderano maggiori dettagli
	 * @return oggetto della classe DetailedMovie, rappresentativo di tutti i
	 *         dettagli inerenti un film
	 */
	@Path("/{id}")
	@GET
	@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
	public Response getDetailedMovie(@PathParam("id") String movieID) {
		MySQLConnection connection = null;

		StatusCode code = StatusCode.OK;
		ResponseBuilder responseBuilder = Response.status(code.getValue());
		try {
			connection = new MySQLConnection();
			int id = Integer.parseInt(movieID);
			DetailedMovie movie = QueryMaker.getDetailedMovie(connection, id);
			if (movie != null)
				responseBuilder.entity(movie);
			else
				code = StatusCode.RESOURCE_NOT_AVAILABLE;
		} catch (Exception e) {
			code = StatusCode.RESOURCE_NOT_AVAILABLE;
		}

		responseBuilder.status(code.getValue());
		if (connection != null)
			connection.closeConnection();
		return responseBuilder.build();
	}

}
